package com.lifesting.tool.j2as3.action;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Stack;

import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.ImportDeclaration;

public final class NodeWriter {
	public static final String LINE_SEPARATOR = System.getProperty("line.separator");
	public static final String CONTENT_KEY="CONTENT_KEY";
	public static final int TAB_SPACE=4;
	public class Writer{
		protected StringBuffer sb = new StringBuffer();
		private int location;
		private Stack<Integer> markers;
		public Writer(int location) {
			super();
			this.location = location;
		}
		
		public int getLocation() {
			return location;
		}
		public void print(String s){
			sb.append(s);
		}

		public Writer printTab(int num){
			char[] chars = new char[num*TAB_SPACE];
			Arrays.fill(chars, ' ');
			sb.append(chars);
			return this;
		}
		public Writer headPrintTab(int num){
			char[] chars = new char[num*TAB_SPACE];
			Arrays.fill(chars, ' ');
			sb.insert(0, chars);
			return this;
		}
		public void println(String s){
			sb.append(s).append(LINE_SEPARATOR);
		}
		public void headPrint(String s){
			sb.insert(0, s);
		}
		public void headPrintln(String s){
			sb.insert(0, s+LINE_SEPARATOR);
		}
		public String getBuffer(){
			return sb.toString();
		}
		public void reset(){
			sb.setLength(0);
		}
		@Override
		public String toString() {
			return "#"+getLocation()+":"+getBuffer();
		}

		public void println() {
			sb.append(LINE_SEPARATOR);			
		}
		public void mark(){
			if(markers == null){
				markers = new Stack<Integer>();
			}
			markers.add(sb.length());
		}
		public String unmark(){
			Integer un = markers.pop();
			String ret = sb.substring(un);
			sb.setLength(sb.length()-ret.length());
			return ret;
		}
	}
	final Writer PACKAGE_WRITE = new Writer(0);
	//can remove/add import
	class ImportWriter extends Writer{
		public ImportWriter() {
			super(1);
		}

		List<String> imports = new ArrayList<String>();

		@Override
		public void println(String s) {
			imports.add(s);
		}
		public void removeImport(String s){
			imports.remove(s);
		}
		@Override
		public String getBuffer() {
			for (String imt : imports){
				printTab(1);
				sb.append(imt).append(LINE_SEPARATOR);
			}
			return sb.toString();
		}
		@Override
		public void reset() {
			super.reset();
			imports.clear();
		}
		
	}
	final ImportWriter IMPORT_WRITER = new ImportWriter();
	public class RootWriter extends Writer{
		public RootWriter() {
			super(-1);
		}
		@Override
		public String getBuffer() {
			headPrint(IMPORT_WRITER.getBuffer());
			headPrint(PACKAGE_WRITE.getBuffer());
			return sb.toString();
		}
	}
	public class WriterFactory{
		HashMap<ASTNode, Writer> created = new HashMap<ASTNode, Writer>();
		public Writer createWriter(ASTNode n){
			Writer exist = created.get(n);
			if (exist == null){
				exist = new Writer(n.getStartPosition());
				created.put(n, exist);
			}
			return exist;
		}
		public void nodeExit(ASTNode node){
			Writer w = created.remove(node);
			if (w != null){
				w.reset();
				w = null;
			}
		}
	}
	WriterFactory WF = new WriterFactory(); 
	Writer ROOT = new RootWriter();
	public void reset()
	{
		for (Writer w : WF.created.values()){
			w.reset();
		}
		WF.created.clear();
		PACKAGE_WRITE.reset();
		IMPORT_WRITER.reset();
		ROOT.reset();
	}
	public void finish(ASTNode node){
		Writer ownWriter = WF.created.get(node);
		if(ownWriter == null){
			return;
		}
		if (node instanceof org.eclipse.jdt.core.dom.PackageDeclaration)
		{
			PACKAGE_WRITE.print(ownWriter.getBuffer());
		}else if (node instanceof ImportDeclaration){
			IMPORT_WRITER.print(ownWriter.getBuffer());
		}else{
			Writer writer = getWriter(node.getParent());
			writer.print(ownWriter.getBuffer());
		}
		WF.nodeExit(node);		
	}
	public Writer getWriter(ASTNode node){
		if (node instanceof CompilationUnit)
			return ROOT;
		return WF.createWriter(node);
	}
	public String getPackage(){
		return PACKAGE_WRITE.getBuffer();
	}
	public String getImports(){
		return IMPORT_WRITER.getBuffer();
	}
	public void removeImport(String s)
	{
		IMPORT_WRITER.removeImport(s);
	}
	public void addImport(String s){
		IMPORT_WRITER.println(s);
	}
	
}
